<html lang="zh-CN"><head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>RFM用户分群分析</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chartjs-chart-matrix@2.0.1/dist/index.umd.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/wordcloud@1.2.0/src/wordcloud2.min.js"></script>
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#165DFF',
                        secondary: '#36CFC9',
                        accent: '#722ED1',
                        warning: '#FF7D00',
                        danger: '#F53F3F',
                        'neutral-100': '#F2F3F5',
                        'neutral-200': '#E5E6EB',
                        'neutral-300': '#C9CDD4',
                        'neutral-400': '#86909C',
                        'neutral-500': '#4E5969',
                        'neutral-600': '#272E3B',
                        'neutral-700': '#1D2129',
                    },
                    fontFamily: {
                        inter: ['Inter', 'system-ui', 'sans-serif'],
                    },
                    boxShadow: {
                        'card': '0 2px 8px rgba(0, 0, 0, 0.08)',
                        'card-hover': '0 4px 16px rgba(0, 0, 0, 0.12)',
                    }
                },
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .card-transition {
                transition: all 0.3s ease;
            }
            .scrollbar-hide::-webkit-scrollbar {
                display: none;
            }
            .scrollbar-hide {
                -ms-overflow-style: none;
                scrollbar-width: none;
            }
            .progress-ring {
                transform: rotate(-90deg);
                transform-origin: 50% 50%;
            }
            .perspective-1000 {
                perspective: 1000px;
            }
            .preserve-3d {
                transform-style: preserve-3d;
            }
            .timeline-dot {
                @apply absolute w-4 h-4 rounded-full bg-primary border-2 border-white z-10;
            }
            .timeline-line {
                @apply absolute w-0.5 bg-neutral-200 z-0;
            }
            .timeline-item {
                @apply relative pl-8 pb-8 last:pb-0;
            }
            .timeline-content {
                @apply bg-white rounded-lg p-4 shadow-sm border border-neutral-100 transition-all hover:shadow-md;
            }
            .slider-track {
                @apply appearance-none h-2 rounded-full bg-neutral-200;
            }
            .slider-thumb {
                @apply appearance-none w-4 h-4 rounded-full bg-primary cursor-pointer;
            }
        }
    </style>
    <style>
        /* 图表容器样式 */
        .chart-container {
            width: 100%;
            height: 100%;
            min-height: 300px;
        }

        /* 2D散点图容器 */
        .scatter-2d-container {
            width: 100%;
            height: 400px;
            position: relative;
        }

        /* 标签云容器 */
        #tag-cloud-container {
            position: relative;
            width: 100%;
            height: 100%;
            min-height: 400px;
        }

        /* 确保canvas元素占满容器 */
        canvas {
            width: 100% !important;
            max-width: 100% !important;
            height: auto !important;
        }

        /* 表格容器优化 */
        .table-container {
            width: 100%;
            overflow-x: auto;
        }

        /* 平滑滚动 */
        html {
            scroll-behavior: smooth;
        }

        /* 维度切换控制按钮 */
        .dimension-controls {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            margin-bottom: 16px;
            justify-content: center;
        }

        .dimension-btn {
            padding: 6px 12px;
            border-radius: 4px;
            border: 1px solid #e2e8f0;
            background: white;
            color: #1e293b;
            cursor: pointer;
            transition: all 0.2s ease;
            font-size: 14px;
        }

        .dimension-btn:hover {
            background: #f1f5f9;
        }

        .dimension-btn.active {
            background: #165DFF;
            color: white;
            border-color: #165DFF;
        }

        /* 行为时间线样式 */
        .timeline-container {
            position: relative;
            padding-left: 1.5rem;
            margin-top: 1rem;
        }

        .timeline-date {
            @apply text-sm font-medium text-primary mb-1;
        }

        .timeline-action {
            @apply font-medium text-neutral-800;
        }

        .timeline-details {
            @apply text-sm text-neutral-500 mt-1;
        }

        /* 用户选择器样式 */
        .user-selector {
            @apply flex flex-col sm:flex-row gap-4 items-start sm:items-center;
        }

        .user-search-container {
            @apply relative flex-grow max-w-md;
        }

        .user-search {
            @apply w-full pl-10 pr-4 py-2 border border-neutral-200 rounded-md focus:ring-2 focus:ring-primary/30 focus:border-primary outline-none transition-all;
        }

        .user-search-icon {
            @apply absolute left-3 top-1/2 transform -translate-y-1/2 text-neutral-400;
        }

        .user-filter-tags {
            @apply flex flex-wrap gap-2 mt-2;
        }

        .filter-tag {
            @apply bg-neutral-100 text-neutral-700 px-3 py-1 rounded-full text-sm flex items-center;
        }

        .filter-tag-remove {
            @apply ml-1 cursor-pointer hover:text-danger;
        }

        /* 加载动画 */
        .loader {
            @apply border-2 border-neutral-200 border-t-primary rounded-full w-6 h-6 animate-spin inline-block;
        }

        /* 日期错误提示样式 */
        .date-error {
            @apply text-danger text-xs bg-danger/10 px-2 py-1 rounded;
        }

        /* 阈值滑块样式 */
        .threshold-container {
            margin-bottom: 1.5rem;
        }

        .threshold-title {
            @apply text-sm font-medium text-neutral-700 mb-2 flex justify-between;
        }

        .threshold-value {
            @apply text-primary font-semibold;
        }

        .threshold-slider {
            width: 100%;
            height: 6px;
            -webkit-appearance: none;
            appearance: none;
            background: #e5e6eb;
            border-radius: 3px;
            outline: none;
        }

        .threshold-slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 18px;
            height: 18px;
            border-radius: 50%;
            background: #165DFF;
            cursor: pointer;
            transition: all 0.15s ease;
        }

        .threshold-slider::-webkit-slider-thumb:hover {
            transform: scale(1.2);
            box-shadow: 0 0 0 4px rgba(22, 93, 255, 0.2);
        }

        .threshold-slider::-moz-range-thumb {
            width: 18px;
            height: 18px;
            border-radius: 50%;
            background: #165DFF;
            cursor: pointer;
            border: none;
            transition: all 0.15s ease;
        }

        .threshold-slider::-moz-range-thumb:hover {
            transform: scale(1.2);
            box-shadow: 0 0 0 4px rgba(22, 93, 255, 0.2);
        }

        .threshold-ticks {
            display: flex;
            justify-content: space-between;
            margin-top: 4px;
            font-size: 12px;
            color: #86909C;
        }

        .threshold-apply-btn {
            @apply bg-secondary hover:bg-secondary/90 text-white px-4 py-2 rounded-md transition-all text-sm mt-2;
        }

        /* 用户列表样式 */
        .user-list-container {
            margin-top: 1rem;
        }

        .user-table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 1rem;
        }

        .user-table th,
        .user-table td {
            padding: 0.75rem 1rem;
            text-align: left;
            border-bottom: 1px solid #e5e6eb;
        }

        .user-table th {
            background-color: #f8fafc;
            font-weight: 600;
            color: #4e5969;
            font-size: 0.875rem;
        }

        .user-table tr:hover {
            background-color: #f8fafc;
        }

        .user-table .view-details-btn {
            color: #165DFF;
            cursor: pointer;
            font-weight: 500;
            font-size: 0.875rem;
        }

        .user-table .view-details-btn:hover {
            text-decoration: underline;
        }

        .user-list-filters {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 1rem;
        }

        .user-list-pagination {
            display: flex;
            justify-content: center;
            margin-top: 1rem;
            gap: 0.5rem;
        }

        .pagination-btn {
            padding: 0.25rem 0.75rem;
            border-radius: 4px;
            border: 1px solid #e2e8f0;
            background: white;
            color: #1e293b;
            cursor: pointer;
            transition: all 0.2s ease;
        }

        .pagination-btn:hover {
            background: #f1f5f9;
        }

        .pagination-btn.active {
            background: #165DFF;
            color: white;
            border-color: #165DFF;
        }
    </style>
</head>
<body class="bg-neutral-100 font-inter text-neutral-700">
    <!-- 导航栏 -->
    <nav class="bg-white shadow-sm sticky top-0 z-10">
        <div class="container mx-auto px-4 sm:px-6 lg:px-8">
            <div class="flex justify-between items-center h-16">
                <div class="flex items-center">
                    <i class="fa fa-bar-chart text-primary text-2xl mr-2"></i>
                    <span class="text-xl font-bold text-neutral-700">用户分析平台</span>
                </div>
                <div class="hidden md:flex space-x-8">
                    <a href="/" class="text-neutral-500 hover:text-primary transition-colors flex items-center">
                        <i class="fa fa-home mr-1"></i> 首页
                    </a>
                    <a href="/rfm_segmentation" class="text-primary font-medium border-b-2 border-primary pb-0.5 flex items-center">
                        <i class="fa fa-users mr-1"></i> RFM分群
                    </a>
                    <a href="/prediction_monitoring" class="text-neutral-500 hover:text-primary transition-colors flex items-center">
                        <i class="fa fa-line-chart mr-1"></i> 预测监控
                    </a>
                </div>
                <div class="md:hidden">
                    <button type="button" class="text-neutral-500 hover:text-primary">
                        <i class="fa fa-bars text-xl"></i>
                    </button>
                </div>
            </div>
        </div>
    </nav>

    <!-- 错误提示区域 -->
    <div id="error-container" class="container mx-auto px-4 py-2 hidden">
        <div id="error-message" class="bg-danger/10 border border-danger/30 text-danger px-4 py-3 rounded-md flex items-center">
            <i class="fa fa-exclamation-circle mr-2"></i>
            <span></span>
        </div>
    </div>

    <!-- 页面标题区域 -->
    <header class="bg-gradient-to-r from-primary to-accent text-white py-12 md:py-20">
        <div class="container mx-auto px-4 sm:px-6 lg:px-8">
            <h1 class="text-3xl md:text-4xl font-bold mb-2">RFM用户分群分析</h1>
            <p class="text-white/80 max-w-3xl">通过最近购买时间(Recency)、购买频率(Frequency)和消费金额(Monetary)三个维度，深入分析用户特征，实现精准用户分群和营销决策</p>
        </div>
    </header>

    <!-- 控制面板 -->
    <div class="container mx-auto px-4 sm:px-6 lg:px-8 -mt-8 relative z-10">
        <div class="bg-white rounded-lg shadow-card p-5 mb-8 card-transition hover:shadow-card-hover">
            <div class="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-6">
                <div class="flex flex-wrap items-start gap-6">
                    <div>
                        <label for="n-clusters" class="block text-sm font-medium text-neutral-500 mb-1">聚类数量</label>
                        <div class="flex items-center">
                            <input type="number" id="n-clusters" value="3" min="2" max="10" class="border border-neutral-200 rounded-md px-3 py-2 w-24 focus:ring-2 focus:ring-primary/30 focus:border-primary outline-none transition-all">
                            <span class="ml-2 text-neutral-400 text-sm">建议2-5个</span>
                        </div>
                    </div>

                    <div>
                        <label for="date-range" class="block text-sm font-medium text-neutral-500 mb-1">分析日期范围</label>
                        <select id="date-range" class="border border-neutral-200 rounded-md px-3 py-2 focus:ring-2 focus:ring-primary/30 focus:border-primary outline-none transition-all">
                            <option value="90">近3个月</option>
                            <option value="180" selected="">近6个月</option>
                            <option value="365">近1年</option>
                            <option value="all">全部时间</option>
                        </select>
                    </div>

                    <div class="lg:flex-1">
                        <label class="block text-sm font-medium text-neutral-500 mb-1">分群阈值调整</label>
                        <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
                            <div class="threshold-container">
                                <div class="threshold-title">
                                    <span>最近购买阈值</span>
                                    <span class="threshold-value" id="recency-threshold-value">30</span>
                                    <span class="text-neutral-500 text-sm">天</span>
                                </div>
                                <input type="range" id="recency-threshold" min="1" max="90" value="30" class="threshold-slider">
                                <div class="threshold-ticks">
                                    <span>1</span>
                                    <span>45</span>
                                    <span>90</span>
                                </div>
                            </div>

                            <div class="threshold-container">
                                <div class="threshold-title">
                                    <span>购买频率阈值</span>
                                    <span class="threshold-value" id="frequency-threshold-value">5</span>
                                    <span class="text-neutral-500 text-sm">次</span>
                                </div>
                                <input type="range" id="frequency-threshold" min="1" max="20" value="5" class="threshold-slider">
                                <div class="threshold-ticks">
                                    <span>1</span>
                                    <span>10</span>
                                    <span>20</span>
                                </div>
                            </div>

                            <div class="threshold-container">
                                <div class="threshold-title">
                                    <span>消费金额阈值</span>
                                    <span class="threshold-value" id="monetary-threshold-value">1000</span>
                                    <span class="text-neutral-500 text-sm">元</span>
                                </div>
                                <input type="range" id="monetary-threshold" min="100" max="5000" step="100" value="1000" class="threshold-slider">
                                <div class="threshold-ticks">
                                    <span>100</span>
                                    <span>2500</span>
                                    <span>5000</span>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="flex flex-col sm:flex-row gap-3">
                    <button id="apply-thresholds" class="threshold-apply-btn flex items-center justify-center">
                        <i class="fa fa-check mr-2"></i> 应用阈值
                    </button>

                    <button id="refresh-rfm" class="bg-primary hover:bg-primary/90 text-white px-5 py-2 rounded-md transition-all flex items-center justify-center">
                        <i class="fa fa-refresh mr-2"></i> 刷新分析
                    </button>
                </div>
            </div>

            <!-- 加载指示器 -->
            <div id="loading-indicator" class="hidden mt-4 flex items-center text-neutral-500">
                <i class="fa fa-spinner fa-spin mr-2"></i>
                <span>数据加载中，请稍候...</span>
            </div>
        </div>
    </div>

    <!-- 用户列表和行为时间线部分 -->
    <div class="container mx-auto px-4 sm:px-6 lg:px-8 mb-8">
        <div class="bg-white rounded-lg shadow-card overflow-hidden card-transition hover:shadow-card-hover">
            <div class="p-5 border-b border-neutral-100">
                <h2 class="text-lg font-semibold text-neutral-700 flex items-center">
                    <i class="fa fa-history text-primary mr-2"></i>
                    用户行为时间线
                </h2>
                <p class="text-neutral-500 text-sm mt-1">查看单个用户的行为轨迹和历史记录</p>
            </div>
            <div class="p-5">
                <!-- 用户选择器 -->
                <div class="user-selector mb-6">
                    <div class="user-search-container">
                        <i class="fa fa-search user-search-icon"></i>
                        <input type="text" id="user-search" placeholder="搜索用户ID..." class="user-search">
                    </div>

                    <div class="flex gap-3">
                        <select id="user-segment-filter" class="border border-neutral-200 rounded-md px-3 py-2 focus:ring-2 focus:ring-primary/30 focus:border-primary outline-none transition-all">
                            <option value="">所有分群用户</option>
                            <option value="高价值">高价值用户</option>
                            <option value="潜力用户">潜力用户</option>
                            <option value="流失风险">流失风险用户</option>
                            <option value="低价值">低价值用户</option>
                        </select>

                        <button id="load-user-timeline" class="bg-primary hover:bg-primary/90 text-white px-4 py-2 rounded-md transition-all flex items-center">
                            <i class="fa fa-user mr-2"></i> 加载用户数据
                        </button>
                    </div>
                </div>

                <div class="user-filter-tags">
                    <div id="active-filters">
                        <!-- 筛选标签将在这里动态显示 -->
                    </div>
                </div>

                <!-- 用户列表部分 -->
                <div class="user-list-container">
                    <h3 class="text-base font-medium text-neutral-700 mb-3">用户列表</h3>

                    <div class="user-list-filters">
                        <div class="text-sm text-neutral-500">显示 <span id="user-count">0</span> 位用户</div>
                        <div>
                            <select id="user-list-sort" class="border border-neutral-200 rounded-md px-2 py-1 text-sm focus:ring-2 focus:ring-primary/30 focus:border-primary outline-none transition-all">
                                <option value="purchase_count_desc">按购买次数 降序</option>
                                <option value="purchase_count_asc">按购买次数 升序</option>
                                <option value="total_amount_desc">按消费总额 降序</option>
                                <option value="total_amount_asc">按消费总额 升序</option>
                            </select>
                        </div>
                    </div>

                    <div class="table-container">
                        <table class="user-table">
                            <thead>
                                <tr>
                                    <th>用户ID</th>
                                    <th>购买次数</th>
                                    <th>购买品类数</th>
                                    <th>消费总额(元)</th>
                                    <th>最近购买时间</th>
                                    <th>操作</th>
                                </tr>
                            </thead>
                            <tbody id="user-list-body">
                                <!-- 用户列表将通过JS动态生成 -->
                            </tbody>
                        </table>
                    </div>

                    <div class="user-list-pagination" id="user-list-pagination">
                        <!-- 分页按钮将通过JS动态生成 -->
                    </div>
                </div>

                <!-- 用户信息概览卡片 -->
                <div id="user-profile-card" class="hidden bg-neutral-50 border border-neutral-100 rounded-lg p-4 mt-6 mb-6">
                    <div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
                        <div>
                            <h3 class="text-lg font-semibold text-neutral-800" id="user-name">用户名称</h3>
                            <p class="text-neutral-500 text-sm" id="user-id">用户ID: </p>
                        </div>
                        <div class="grid grid-cols-2 md:grid-cols-3 gap-4 w-full md:w-auto">
                            <div class="bg-white p-3 rounded shadow-sm">
                                <div class="text-xs text-neutral-500">购买次数</div>
                                <div class="text-lg font-semibold text-primary" id="user-purchase-count">-</div>
                            </div>
                            <div class="bg-white p-3 rounded shadow-sm">
                                <div class="text-xs text-neutral-500">购买品类</div>
                                <div class="text-lg font-semibold text-primary" id="user-category-count">-</div>
                            </div>
                            <div class="bg-white p-3 rounded shadow-sm">
                                <div class="text-xs text-neutral-500">消费总额</div>
                                <div class="text-lg font-semibold text-primary" id="user-total-amount">-</div>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- 时间线加载状态 -->
                <div id="timeline-loading" class="hidden py-12 flex flex-col items-center justify-center">
                    <div class="loader mb-3"></div>
                    <p class="text-neutral-500">加载用户行为数据中...</p>
                </div>

                <!-- 时间线空状态 -->
                <div id="timeline-empty" class="hidden py-12 flex flex-col items-center justify-center text-neutral-500">
                    <i class="fa fa-calendar-o text-4xl mb-3"></i>
                    <p>未找到用户行为数据，请选择一个有效的用户ID</p>
                    <p class="text-sm mt-2">可用用户ID: 486458, 337305, 702034, 682594, 735254, 961473, 127591, 660805, 1002351, 435981</p>
                </div>

                <!-- 行为时间线容器 -->
                <div id="user-timeline-container" class="hidden timeline-container">
                    <!-- 时间线将通过JS动态生成 -->
                </div>
            </div>
        </div>
    </div>

    <!-- RFM分群页面内容 -->
    <main class="container mx-auto px-4 sm:px-6 lg:px-8 pb-16">
        <!-- 分析内容网格布局 -->
        <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
            <!-- RFM得分分布 -->
            <div class="lg:col-span-3 bg-white rounded-lg shadow-card overflow-hidden card-transition hover:shadow-card-hover">
                <div class="p-5 border-b border-neutral-100">
                    <h2 class="text-lg font-semibold text-neutral-700 flex items-center">
                        <i class="fa fa-bar-chart text-primary mr-2"></i>
                        RFM得分分布
                    </h2>
                    <p class="text-neutral-500 text-sm mt-1">查看用户最近购买、购买频率和消费金额的得分分布情况</p>
                </div>
                <div class="p-5">
                    <div class="chart-container h-[400px]">
                        <canvas id="rfm-score-chart"></canvas>
                    </div>
                </div>
            </div>

            <!-- RFM二维分布散点图（带切换按钮） -->
            <div class="lg:col-span-3 bg-white rounded-lg shadow-card overflow-hidden card-transition hover:shadow-card-hover">
                <div class="p-5 border-b border-neutral-100">
                    <h2 class="text-lg font-semibold text-neutral-700 flex items-center">
                        <i class="fa fa-area-chart text-primary mr-2"></i>
                        RFM二维分布
                    </h2>
                    <p class="text-neutral-500 text-sm mt-1">用户在RFM不同维度组合上的分布情况</p>
                </div>
                <div class="p-5">
                    <!-- 维度切换按钮 -->
                    <div class="dimension-controls">
                        <button class="dimension-btn active" data-dimensions="recency-frequency">最近购买 vs 购买频率</button>
                        <button class="dimension-btn" data-dimensions="recency-monetary">最近购买 vs 消费金额</button>
                        <button class="dimension-btn" data-dimensions="frequency-monetary">购买频率 vs 消费金额</button>
                    </div>

                    <!-- 散点图容器 -->
                    <div class="scatter-2d-container">
                        <canvas id="rfm-2d-scatter-chart"></canvas>
                    </div>
                </div>
            </div>

            <!-- 用户分群占比 -->
            <div class="lg:col-span-1 bg-white rounded-lg shadow-card overflow-hidden card-transition hover:shadow-card-hover">
                <div class="p-5 border-b border-neutral-100">
                    <h2 class="text-lg font-semibold text-neutral-700 flex items-center">
                        <i class="fa fa-pie-chart text-primary mr-2"></i>
                        用户分群占比
                    </h2>
                    <p class="text-neutral-500 text-sm mt-1">各用户分群的数量占比情况统计</p>
                </div>
                <div class="p-5">
                    <div class="chart-container h-[300px]">
                        <canvas id="segment-pie-chart"></canvas>
                    </div>
                </div>
            </div>

            <!-- 用户画像标签云 -->
            <div class="lg:col-span-2 bg-white rounded-lg shadow-card overflow-hidden card-transition hover:shadow-card-hover">
                <div class="p-5 border-b border-neutral-100">
                    <div class="flex flex-wrap items-center justify-between gap-2">
                        <div>
                            <h2 class="text-lg font-semibold text-neutral-700 flex items-center">
                                <i class="fa fa-tags text-primary mr-2"></i>
                                用户画像标签云
                            </h2>
                            <p class="text-neutral-500 text-sm mt-1">分析用户群体的价格敏感度和活跃度特征标签</p>
                        </div>
                        <div class="flex gap-2">
                            <button class="tag-cloud-btn bg-primary text-white px-3 py-1 rounded-md text-sm" data-type="all">全部</button>
                            <button class="tag-cloud-btn bg-neutral-100 text-neutral-700 px-3 py-1 rounded-md text-sm" data-type="price">价格敏感度</button>
                            <button class="tag-cloud-btn bg-neutral-100 text-neutral-700 px-3 py-1 rounded-md text-sm" data-type="activity">活跃度</button>
                        </div>
                    </div>
                </div>
                <div class="p-5">
                    <div id="tag-cloud-container" class="border border-neutral-100 rounded-lg overflow-hidden">
                        <div id="tag-cloud" class="w-full h-full"></div>
                    </div>
                </div>
            </div>

            <!-- 用户画像分布 -->
            <div class="lg:col-span-3 bg-white rounded-lg shadow-card overflow-hidden card-transition hover:shadow-card-hover">
                <div class="p-5 border-b border-neutral-100">
                    <h2 class="text-lg font-semibold text-neutral-700 flex items-center">
                        <i class="fa fa-signal text-primary mr-2"></i>
                        用户画像分布
                    </h2>
                    <p class="text-neutral-500 text-sm mt-1">用户群体的各项画像特征分布统计</p>
                </div>
                <div class="p-5">
                    <div class="chart-container h-[400px]">
                        <canvas id="user-portrait-chart"></canvas>
                    </div>
                </div>
            </div>

            <!-- 分群结果卡片 -->
            <div class="lg:col-span-3 bg-white rounded-lg shadow-card overflow-hidden card-transition hover:shadow-card-hover">
                <div class="p-5 border-b border-neutral-100">
                    <h2 class="text-lg font-semibold text-neutral-700 flex items-center">
                        <i class="fa fa-th-large text-primary mr-2"></i>
                        分群结果卡片
                    </h2>
                    <p class="text-neutral-500 text-sm mt-1">各用户群体占比及策略建议</p>
                </div>
                <div class="p-5">
                    <div id="segments-cards-container" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
                        <!-- 分群卡片将通过JS动态填充 -->
                    </div>
                </div>
            </div>
        </div>
    </main>

    <!-- 页脚 -->
    <footer class="bg-white border-t border-neutral-200 py-8">
        <div class="container mx-auto px-4 sm:px-6 lg:px-8">
            <div class="flex flex-col md:flex-row justify-between items-center">
                <div class="mb-4 md:mb-0">
                    <p class="text-neutral-500 text-sm">© 2023 用户分析平台. 保留所有权利.</p>
                </div>
                <div class="flex space-x-6">
                    <a href="#" class="text-neutral-400 hover:text-primary transition-colors">
                        <i class="fa fa-question-circle"></i> 帮助中心
                    </a>
                    <a href="#" class="text-neutral-400 hover:text-primary transition-colors">
                        <i class="fa fa-file-text-o"></i> 使用文档
                    </a>
                    <a href="#" class="text-neutral-400 hover:text-primary transition-colors">
                        <i class="fa fa-envelope-o"></i> 联系我们
                    </a>
                </div>
            </div>
        </div>
    </footer>

    <script>
        // 全局变量存储当前显示的维度组合
        let currentDimensions = 'recency-frequency';
        let rfmData = null;
        // 存储当前阈值设置
        let currentThresholds = {
            recency: 30,
            frequency: 5,
            monetary: 1000
        };

        // 分页相关变量
        let currentPage = 1;
        const usersPerPage = 5;
        let allUsers = [];

        // 静态用户行为数据 - 直接使用提供的数据
        const userBehaviorData = [
            {排名: 1, 用户ID: 486458, 购买商品品类数: 5, 购买时间: "2017-11-05 09:23", 价格: 689},
            {排名: 1, 用户ID: 486458, 购买商品品类数: 5, 购买时间: "2017-11-12 15:47", 价格: 324},
            {排名: 1, 用户ID: 486458, 购买商品品类数: 5, 购买时间: "2017-11-28 18:12", 价格: 876},
            {排名: 1, 用户ID: 486458, 购买商品品类数: 5, 购买时间: "2017-12-03 10:05", 价格: 459},
            {排名: 1, 用户ID: 486458, 购买商品品类数: 5, 购买时间: "2017-12-22 14:30", 价格: 199},
            {排名: 2, 用户ID: 337305, 购买商品品类数: 4, 购买时间: "2017-11-08 11:55", 价格: 753},
            {排名: 2, 用户ID: 337305, 购买商品品类数: 4, 购买时间: "2017-11-19 08:32", 价格: 542},
            {排名: 2, 用户ID: 337305, 购买商品品类数: 4, 购买时间: "2017-12-10 16:48", 价格: 276},
            {排名: 2, 用户ID: 337305, 购买商品品类数: 4, 购买时间: "2017-12-29 19:05", 价格: 912},
            {排名: 3, 用户ID: 702034, 购买商品品类数: 4, 购买时间: "2017-11-02 13:20", 价格: 405},
            {排名: 3, 用户ID: 702034, 购买商品品类数: 4, 购买时间: "2017-11-25 17:15", 价格: 632},
            {排名: 3, 用户ID: 702034, 购买商品品类数: 4, 购买时间: "2017-12-07 09:43", 价格: 887},
            {排名: 3, 用户ID: 702034, 购买商品品类数: 4, 购买时间: "2017-12-18 12:50", 价格: 319},
            {排名: 4, 用户ID: 682594, 购买商品品类数: 3, 购买时间: "2017-11-15 14:22", 价格: 567},
            {排名: 4, 用户ID: 682594, 购买商品品类数: 3, 购买时间: "2017-12-01 10:30", 价格: 721},
            {排名: 4, 用户ID: 682594, 购买商品品类数: 3, 购买时间: "2017-12-15 16:18", 价格: 245},
            {排名: 5, 用户ID: 735254, 购买商品品类数: 3, 购买时间: "2017-11-09 08:55", 价格: 903},
            {排名: 5, 用户ID: 735254, 购买商品品类数: 3, 购买时间: "2017-11-23 15:40", 价格: 489},
            {排名: 5, 用户ID: 735254, 购买商品品类数: 3, 购买时间: "2017-12-24 19:25", 价格: 176},
            {排名: 6, 用户ID: 961473, 购买商品品类数: 3, 购买时间: "2017-11-17 11:30", 价格: 654},
            {排名: 6, 用户ID: 961473, 购买商品品类数: 3, 购买时间: "2017-12-05 13:15", 价格: 387},
            {排名: 6, 用户ID: 961473, 购买商品品类数: 3, 购买时间: "2017-12-20 18:40", 价格: 822},
            {排名: 7, 用户ID: 127591, 购买商品品类数: 2, 购买时间: "2017-11-04 16:25", 价格: 512},
            {排名: 7, 用户ID: 127591, 购买商品品类数: 2, 购买时间: "2017-12-12 09:10", 价格: 765},
            {排名: 8, 用户ID: 660805, 购买商品品类数: 2, 购买时间: "2017-11-21 14:35", 价格: 298},
            {排名: 8, 用户ID: 660805, 购买商品品类数: 2, 购买时间: "2017-12-09 17:50", 价格: 943},
            {排名: 9, 用户ID: 1002351, 购买商品品类数: 2, 购买时间: "2017-11-30 10:45", 价格: 431},
            {排名: 9, 用户ID: 1002351, 购买商品品类数: 2, 购买时间: "2017-12-26 15:20", 价格: 678},
            {排名: 10, 用户ID: 435981, 购买商品品类数: 2, 购买时间: "2017-11-14 08:20", 价格: 812},
            {排名: 10, 用户ID: 435981, 购买商品品类数: 2, 购买时间: "2017-12-04 13:55", 价格: 356}
        ];

        // 处理用户数据，生成用户列表
        function processUserData() {
            // 按用户ID分组
            const userGroups = {};

            userBehaviorData.forEach(item => {
                const userId = item['用户ID'];
                if (!userGroups[userId]) {
                    userGroups[userId] = {
                        userId: userId,
                        purchaseCount: 0,
                        categoryCount: item['购买商品品类数'],
                        totalAmount: 0,
                        lastPurchaseTime: null,
                        purchases: []
                    };
                }

                // 更新用户数据
                userGroups[userId].purchaseCount++;
                userGroups[userId].totalAmount += item['价格'];
                userGroups[userId].purchases.push({
                    time: item['购买时间'],
                    price: item['价格']
                });

                // 更新最近购买时间
                const currentTime = new Date(item['购买时间']).getTime();
                if (!userGroups[userId].lastPurchaseTime || currentTime > new Date(userGroups[userId].lastPurchaseTime).getTime()) {
                    userGroups[userId].lastPurchaseTime = item['购买时间'];
                }
            });

            // 转换为数组并添加用户分群信息
            allUsers = Object.values(userGroups).map(user => {
                // 简单的分群逻辑
                let segment = '';
                if (user.purchaseCount >= 4 && user.totalAmount >= 2000) {
                    segment = '高价值用户';
                } else if (user.purchaseCount >= 3 && user.totalAmount >= 1000) {
                    segment = '潜力用户';
                } else if (user.purchaseCount >= 2) {
                    // 检查最近购买时间是否超过阈值
                    const lastPurchase = new Date(user.lastPurchaseTime);
                    const daysSinceLastPurchase = Math.floor((new Date() - lastPurchase) / (1000 * 60 * 60 * 24));
                    if (daysSinceLastPurchase > currentThresholds.recency) {
                        segment = '流失风险用户';
                    } else {
                        segment = '潜力用户';
                    }
                } else {
                    segment = '低价值用户';
                }

                return {
                    ...user,
                    segment: segment
                };
            });

            // 初始排序
            sortUsers('purchase_count_desc');
        }

        // 排序用户列表
        function sortUsers(sortType) {
            switch(sortType) {
                case 'purchase_count_desc':
                    allUsers.sort((a, b) => b.purchaseCount - a.purchaseCount);
                    break;
                case 'purchase_count_asc':
                    allUsers.sort((a, b) => a.purchaseCount - b.purchaseCount);
                    break;
                case 'total_amount_desc':
                    allUsers.sort((a, b) => b.totalAmount - a.totalAmount);
                    break;
                case 'total_amount_asc':
                    allUsers.sort((a, b) => a.totalAmount - b.totalAmount);
                    break;
            }

            // 重新渲染当前页
            renderUserList();
        }

        // 渲染用户列表
        function renderUserList() {
            const userListBody = document.getElementById('user-list-body');
            const paginationContainer = document.getElementById('user-list-pagination');
            const userCountElement = document.getElementById('user-count');

            // 更新用户总数
            userCountElement.textContent = allUsers.length;

            // 清空现有内容
            userListBody.innerHTML = '';

            // 计算当前页的用户
            const startIndex = (currentPage - 1) * usersPerPage;
            const endIndex = Math.min(startIndex + usersPerPage, allUsers.length);
            const currentUsers = allUsers.slice(startIndex, endIndex);

            // 生成表格行
            currentUsers.forEach(user => {
                const row = document.createElement('tr');
                row.innerHTML = `
                    <td>${user.userId}</td>
                    <td>${user.purchaseCount}</td>
                    <td>${user.categoryCount}</td>
                    <td>${user.totalAmount.toFixed(2)}</td>
                    <td>${formatDate(user.lastPurchaseTime).formatted}</td>
                    <td>
                        <span class="view-details-btn" data-user-id="${user.userId}">查看详情</span>
                    </td>
                `;
                userListBody.appendChild(row);

                // 添加点击事件
                row.querySelector('.view-details-btn').addEventListener('click', function() {
                    const userId = this.dataset.userId;
                    // 填充搜索框
                    document.getElementById('user-search').value = userId;
                    // 触发加载用户时间线
                    document.getElementById('load-user-timeline').click();
                    // 滚动到时间线
                    document.getElementById('user-timeline-container').scrollIntoView({ behavior: 'smooth' });
                });
            });

            // 生成分页按钮
            renderPagination();
        }

        // 渲染分页按钮
        function renderPagination() {
            const paginationContainer = document.getElementById('user-list-pagination');
            paginationContainer.innerHTML = '';

            const totalPages = Math.ceil(allUsers.length / usersPerPage);

            // 首页按钮
            const firstBtn = document.createElement('button');
            firstBtn.className = `pagination-btn ${currentPage === 1 ? 'opacity-50 cursor-not-allowed' : ''}`;
            firstBtn.textContent = '首页';
            firstBtn.addEventListener('click', () => {
                if (currentPage !== 1) {
                    currentPage = 1;
                    renderUserList();
                }
            });
            paginationContainer.appendChild(firstBtn);

            // 上一页按钮
            const prevBtn = document.createElement('button');
            prevBtn.className = `pagination-btn ${currentPage === 1 ? 'opacity-50 cursor-not-allowed' : ''}`;
            prevBtn.textContent = '上一页';
            prevBtn.addEventListener('click', () => {
                if (currentPage > 1) {
                    currentPage--;
                    renderUserList();
                }
            });
            paginationContainer.appendChild(prevBtn);

            // 页码按钮
            for (let i = 1; i <= totalPages; i++) {
                const pageBtn = document.createElement('button');
                pageBtn.className = `pagination-btn ${currentPage === i ? 'active' : ''}`;
                pageBtn.textContent = i;
                pageBtn.addEventListener('click', () => {
                    currentPage = i;
                    renderUserList();
                });
                paginationContainer.appendChild(pageBtn);
            }

            // 下一页按钮
            const nextBtn = document.createElement('button');
            nextBtn.className = `pagination-btn ${currentPage === totalPages ? 'opacity-50 cursor-not-allowed' : ''}`;
            nextBtn.textContent = '下一页';
            nextBtn.addEventListener('click', () => {
                if (currentPage < totalPages) {
                    currentPage++;
                    renderUserList();
                }
            });
            paginationContainer.appendChild(nextBtn);

            // 末页按钮
            const lastBtn = document.createElement('button');
            lastBtn.className = `pagination-btn ${currentPage === totalPages ? 'opacity-50 cursor-not-allowed' : ''}`;
            lastBtn.textContent = '末页';
            lastBtn.addEventListener('click', () => {
                if (currentPage !== totalPages) {
                    currentPage = totalPages;
                    renderUserList();
                }
            });
            paginationContainer.appendChild(lastBtn);
        }

        // 显示错误消息
        function showError(message) {
            const errorContainer = document.getElementById('error-container');
            const errorMessage = document.getElementById('error-message').querySelector('span');
            errorMessage.textContent = message;
            errorContainer.classList.remove('hidden');

            // 添加淡入效果
            errorContainer.classList.add('animate-fade-in');

            setTimeout(() => {
                // 添加淡出效果
                errorContainer.classList.add('opacity-0', 'transition-opacity', 'duration-500');
                setTimeout(() => {
                    errorContainer.classList.add('hidden');
                    errorContainer.classList.remove('opacity-0', 'transition-opacity', 'duration-500', 'animate-fade-in');
                }, 500);
            }, 5000);
        }

        // 调整所有图表大小
        function resizeCharts() {
            if (window.rfmScoreChart) window.rfmScoreChart.resize();
            if (window.segmentPieChart) window.segmentPieChart.resize();
            if (window.userPortraitChart) window.userPortraitChart.resize();
            if (window.rfm2DChart) window.rfm2DChart.resize();

            // 重新渲染标签云以适应新尺寸
            if (window.userPortraitData) {
                const activeBtn = document.querySelector('.tag-cloud-btn.bg-primary');
                const type = activeBtn ? activeBtn.dataset.type : 'all';
                renderTagCloud(window.userPortraitData, type);
            }
        }

        // 安全的日期格式化函数
        function formatDate(timestamp, options = {}) {
            // 处理空值或无效值
            if (!timestamp || timestamp === 'undefined' || timestamp === 'null') {
                return {
                    formatted: '日期未知',
                    isValid: false
                };
            }

            // 尝试将时间戳转换为日期对象
            let date;
            if (typeof timestamp === 'string') {
                // 处理可能的时间戳字符串
                if (!isNaN(timestamp)) {
                    // 如果是数字字符串，尝试作为时间戳处理
                    date = new Date(parseInt(timestamp));
                } else {
                    // 否则尝试直接解析
                    date = new Date(timestamp);
                }
            } else if (typeof timestamp === 'number') {
                date = new Date(timestamp);
            } else {
                date = new Date(timestamp);
            }

            // 检查日期是否有效
            if (isNaN(date.getTime())) {
                return {
                    formatted: `无效日期: ${timestamp}`,
                    isValid: false
                };
            }

            // 默认选项
            const defaultOptions = {
                date: { year: 'numeric', month: '2-digit', day: '2-digit' },
                time: { hour: '2-digit', minute: '2-digit' }
            };

            // 合并选项
            const dateOptions = options.date || defaultOptions.date;
            const timeOptions = options.time || defaultOptions.time;

            return {
                formatted: date.toLocaleDateString('zh-CN', dateOptions),
                timeFormatted: date.toLocaleTimeString('zh-CN', timeOptions),
                timestamp: date.getTime(),
                isValid: true
            };
        }

        // 自定义JSON解析器，处理NaN值
        function safeJsonParse(jsonString) {
            try {
                // 替换NaN为null，因为NaN不是有效的JSON值
                return JSON.parse(jsonString.replace(/\bNaN\b/g, "null"));
            } catch (error) {
                console.error('JSON解析错误:', error);
                console.error('原始数据:', jsonString);

                // 尝试更严格的错误恢复
                try {
                    // 替换所有无效值
                    const sanitized = jsonString
                        .replace(/\bNaN\b/g, "null")
                        .replace(/\bInfinity\b/g, "null")
                        .replace(/\bundefined\b/g, "null");
                    return JSON.parse(sanitized);
                } catch (e) {
                    console.error('尝试修复后仍解析失败:', e);
                    throw new Error(`JSON解析错误: ${e.message}`);
                }
            }
        }

        // 页面加载完成后初始化
        document.addEventListener('DOMContentLoaded', function() {
            // 初始化用户数据并渲染用户列表
            processUserData();
            renderUserList();

            // 初始化排序事件
            document.getElementById('user-list-sort').addEventListener('change', function() {
                sortUsers(this.value);
            });

            // 初始化阈值滑块事件
            initThresholdSliders();

            // 初始化维度切换事件监听
            initDimensionSwitchEvents();

            // 初始化用户行为时间线相关事件
            initUserTimelineEvents();

            // 加载RFM数据
            loadRfmData();
            loadUserSegments();
            loadUserPortraitData();

            // 绑定刷新按钮事件
            document.getElementById('refresh-rfm').addEventListener('click', function() {
                refreshAnalysis();
            });

            // 为聚类数量输入框添加回车键触发刷新
            document.getElementById('n-clusters').addEventListener('keyup', function(event) {
                if (event.key === 'Enter') {
                    refreshAnalysis();
                }
            });

            // 标签云类型切换
            document.querySelectorAll('.tag-cloud-btn').forEach(btn => {
                btn.addEventListener('click', function() {
                    document.querySelectorAll('.tag-cloud-btn').forEach(b => {
                        b.classList.remove('bg-primary', 'text-white');
                        b.classList.add('bg-neutral-100', 'text-neutral-700');
                    });
                    this.classList.remove('bg-neutral-100', 'text-neutral-700');
                    this.classList.add('bg-primary', 'text-white');

                    if (window.userPortraitData) {
                        renderTagCloud(window.userPortraitData, this.dataset.type);
                    }
                });
            });

            // 监听窗口大小变化，重新渲染图表
            window.addEventListener('resize', function() {
                resizeCharts();
            });
        });

        // 初始化阈值滑块事件
        function initThresholdSliders() {
            // 获取滑块元素
            const recencySlider = document.getElementById('recency-threshold');
            const frequencySlider = document.getElementById('frequency-threshold');
            const monetarySlider = document.getElementById('monetary-threshold');

            // 获取显示值的元素
            const recencyValue = document.getElementById('recency-threshold-value');
            const frequencyValue = document.getElementById('frequency-threshold-value');
            const monetaryValue = document.getElementById('monetary-threshold-value');

            // 更新显示值
            function updateThresholdValue(slider, displayElement) {
                displayElement.textContent = slider.value;
            }

            // 滑块事件监听
            recencySlider.addEventListener('input', function() {
                updateThresholdValue(this, recencyValue);
                currentThresholds.recency = parseInt(this.value);
                // 更新用户分群信息
                processUserData();
            });

            frequencySlider.addEventListener('input', function() {
                updateThresholdValue(this, frequencyValue);
                currentThresholds.frequency = parseInt(this.value);
            });

            monetarySlider.addEventListener('input', function() {
                updateThresholdValue(this, monetaryValue);
                currentThresholds.monetary = parseInt(this.value);
            });

            // 应用阈值按钮事件
            document.getElementById('apply-thresholds').addEventListener('click', function() {
                // 显示加载指示器
                document.getElementById('loading-indicator').classList.remove('hidden');

                // 应用新阈值并刷新分析
                loadRfmData().then(() => {
                    loadUserSegments().then(() => {
                        loadUserPortraitData().then(() => {
                            // 隐藏加载指示器
                            document.getElementById('loading-indicator').classList.add('hidden');
                            showError('阈值已应用，分析数据已更新', 'success');
                        });
                    });
                });
            });
        }

        // 刷新分析数据
        function refreshAnalysis() {
            // 显示加载指示器
            document.getElementById('loading-indicator').classList.remove('hidden');

            // 加载数据
            loadRfmData().then(() => {
                loadUserSegments().then(() => {
                    loadUserPortraitData().then(() => {
                        // 隐藏加载指示器
                        document.getElementById('loading-indicator').classList.add('hidden');
                    });
                });
            });
        }

        // 初始化维度切换事件监听
        function initDimensionSwitchEvents() {
            document.querySelectorAll('.dimension-btn').forEach(btn => {
                btn.addEventListener('click', function() {
                    // 更新按钮状态
                    document.querySelectorAll('.dimension-btn').forEach(b => {
                        b.classList.remove('active');
                    });
                    this.classList.add('active');

                    // 更新当前维度
                    currentDimensions = this.dataset.dimensions;

                    // 如果已有数据，重新渲染图表
                    if (rfmData) {
                        renderRfm2DScatterChart(rfmData);
                    }
                });
            });
        }

        // 初始化用户行为时间线相关事件
        function initUserTimelineEvents() {
            // 加载用户时间线按钮点击事件
            document.getElementById('load-user-timeline').addEventListener('click', function() {
                const userId = document.getElementById('user-search').value.trim();
                const segmentFilter = document.getElementById('user-segment-filter').value;

                // 验证用户ID是否为有效数字
                if (!userId || isNaN(userId) || parseInt(userId) <= 0) {
                    showError('请输入有效的用户ID');
                    return;
                }

                // 显示加载状态
                document.getElementById('timeline-loading').classList.remove('hidden');
                document.getElementById('timeline-empty').classList.add('hidden');
                document.getElementById('user-timeline-container').classList.add('hidden');
                document.getElementById('user-profile-card').classList.add('hidden');

                // 从静态数据加载用户行为时间线
                loadUserTimelineFromStaticData(userId, segmentFilter);

                // 更新活跃筛选标签
                updateActiveFilters(userId, segmentFilter);
            });

            // 用户搜索框回车键触发
            document.getElementById('user-search').addEventListener('keyup', function(event) {
                if (event.key === 'Enter') {
                    document.getElementById('load-user-timeline').click();
                }
            });
        }

        // 从静态数据加载用户行为时间线
        function loadUserTimelineFromStaticData(userId, segmentFilter) {
            // 转换为数字进行比较
            const targetUserId = parseInt(userId);

            // 从静态数据中筛选特定用户的记录
            const userEvents = userBehaviorData
                .filter(item => item.用户ID === targetUserId)
                .map(item => ({
                    type: 'purchase',
                    timestamp: item.购买时间,
                    amount: item.价格,
                    order_id: `${Math.floor(Math.random() * 1000000)}`,
                    products: [{ name: `商品${Math.floor(Math.random() * 1000)}` }],
                    quantity: 1
                }));

            // 隐藏加载状态
            document.getElementById('timeline-loading').classList.add('hidden');

            if (userEvents.length > 0) {
                // 计算用户统计信息
                const purchaseCount = userEvents.length;
                const totalAmount = userEvents.reduce((sum, event) => sum + event.amount, 0);
                const categoryCount = userBehaviorData.find(item => item.用户ID === targetUserId)?.购买商品品类数 || 0;

                // 更新用户信息卡片（已移除最近购买信息）
                document.getElementById('user-name').textContent = `用户 ${targetUserId}`;
                document.getElementById('user-id').textContent = `用户ID: ${targetUserId}`;
                document.getElementById('user-purchase-count').textContent = purchaseCount;
                document.getElementById('user-category-count').textContent = categoryCount;
                document.getElementById('user-total-amount').textContent = `¥${totalAmount.toFixed(2)}`;
                document.getElementById('user-profile-card').classList.remove('hidden');

                // 检查并处理无效日期
                const validEvents = userEvents.filter(event => {
                    const dateResult = formatDate(event.timestamp);
                    return dateResult.isValid;
                });

                // 如果过滤后没有有效事件，显示警告
                if (validEvents.length === 0) {
                    showError('所有事件的日期数据都无效，无法显示时间线');
                    document.getElementById('timeline-empty').classList.remove('hidden');
                    return;
                }

                // 渲染时间线
                renderUserTimeline(validEvents);
                // 显示时间线容器，隐藏空状态
                document.getElementById('user-timeline-container').classList.remove('hidden');
                document.getElementById('timeline-empty').classList.add('hidden');
            } else {
                // 显示空状态
                document.getElementById('timeline-empty').classList.remove('hidden');
                document.getElementById('user-timeline-container').classList.add('hidden');
                document.getElementById('user-profile-card').classList.add('hidden');
                showError(`未找到用户ID为 ${userId} 的数据`);
            }
        }

        // 更新活跃筛选标签
        function updateActiveFilters(userId, segmentFilter) {
            const filterContainer = document.getElementById('active-filters');
            filterContainer.innerHTML = '';
            filterContainer.innerHTML = '';

            // 添加用户筛选标签
            if (userId) {
                const userFilterTag = document.createElement('div');
                userFilterTag.className = 'filter-tag';
                userFilterTag.innerHTML = `
                    用户ID: ${userId}
                    <span class="filter-tag-remove" data-type="user">
                        <i class="fa fa-times-circle"></i>
                    </span>
                `;
                filterContainer.appendChild(userFilterTag);
            }

            // 添加分群筛选标签
            if (segmentFilter) {
                const segmentFilterTag = document.createElement('div');
                segmentFilterTag.className = 'filter-tag';
                segmentFilterTag.innerHTML = `
                    分群: ${segmentFilter}
                    <span class="filter-tag-remove" data-type="segment">
                        <i class="fa fa-times-circle"></i>
                    </span>
                `;
                filterContainer.appendChild(segmentFilterTag);
            }

            // 为筛选标签添加删除事件
            document.querySelectorAll('.filter-tag-remove').forEach(removeBtn => {
                removeBtn.addEventListener('click', function() {
                    const type = this.dataset.type;
                    if (type === 'user') {
                        document.getElementById('user-search').value = '';
                    } else if (type === 'segment') {
                        document.getElementById('user-segment-filter').value = '';
                    }
                    updateActiveFilters(
                        document.getElementById('user-search').value.trim(),
                        document.getElementById('user-segment-filter').value
                    );
                });
            });
        }

        // 渲染用户信息概览
        function renderUserProfile(userData) {
            if (!userData) return;

            document.getElementById('user-profile-card').classList.remove('hidden');
            document.getElementById('user-name').textContent = userData.name || '未知用户';
            document.getElementById('user-id').textContent = `用户ID: ${userData.id || '未知'}`;

            // 已移除最近购买相关数据显示
            document.getElementById('user-purchase-count').textContent = userData.frequency || 'N/A';
            document.getElementById('user-category-count').textContent = userData.category_count || 'N/A';
            document.getElementById('user-total-amount').textContent = userData.monetary
                ? `¥${userData.monetary.toFixed(2)}`
                : 'N/A';
        }

        // 渲染用户行为时间线
        function renderUserTimeline(events) {
            const container = document.getElementById('user-timeline-container');
            container.innerHTML = '';

            // 按时间排序，最新的在前
            events.sort((a, b) => {
                const dateA = formatDate(a.timestamp);
                const dateB = formatDate(b.timestamp);
                return dateB.timestamp - dateA.timestamp;
            });

            // 按日期分组
            const eventsByDate = {};
            events.forEach(event => {
                const date = formatDate(event.timestamp);
                if (!eventsByDate[date.formatted]) {
                    eventsByDate[date.formatted] = [];
                }
                eventsByDate[date.formatted].push(event);
            });

            // 获取所有日期并排序（最新的在前）
            const dates = Object.keys(eventsByDate).sort((a, b) => {
                // 获取每个日期的第一个事件的时间戳进行比较
                const firstEventA = eventsByDate[a][0];
                const firstEventB = eventsByDate[b][0];
                const dateA = formatDate(firstEventA.timestamp);
                const dateB = formatDate(firstEventB.timestamp);
                return dateB.timestamp - dateA.timestamp;
            });

            // 创建时间线线条
            const line = document.createElement('div');
            line.className = 'timeline-line';
            line.style.height = `${dates.length * 120}px`; // 估算高度
            line.style.top = '0';
            line.style.left = '0';
            container.appendChild(line);

            // 为每一天创建时间线项
            dates.forEach((date, index) => {
                const dayEvents = eventsByDate[date];

                // 创建时间线项容器
                const timelineItem = document.createElement('div');
                timelineItem.className = 'timeline-item';

                // 添加时间点
                const dot = document.createElement('div');
                dot.className = 'timeline-dot';
                dot.style.top = '0';
                dot.style.left = '-9px';
                timelineItem.appendChild(dot);

                // 添加日期标题
                const dateHeader = document.createElement('div');
                dateHeader.className = 'timeline-date';
                dateHeader.textContent = date;
                timelineItem.appendChild(dateHeader);

                // 添加当天的所有事件
                dayEvents.forEach(event => {
                    const eventElement = createTimelineEvent(event);
                    timelineItem.appendChild(eventElement);
                });

                container.appendChild(timelineItem);
            });
        }

        // 创建单个时间线事件元素
        function createTimelineEvent(event) {
            const eventElement = document.createElement('div');
            eventElement.className = 'timeline-content mb-3 last:mb-0';

            // 根据事件类型选择不同的图标
            let icon = 'fa-circle-o';
            switch(event.type) {
                case 'purchase':
                    icon = 'fa-shopping-cart text-primary';
                    break;
                case 'view':
                    icon = 'fa-eye text-neutral-500';
                    break;
                case 'click':
                    icon = 'fa-mouse-pointer text-secondary';
                    break;
                case 'add_to_cart':
                    icon = 'fa-cart-plus text-warning';
                    break;
                case 'login':
                    icon = 'fa-sign-in text-accent';
                    break;
                case 'logout':
                    icon = 'fa-sign-out text-neutral-400';
                    break;
            }

            // 格式化时间，使用安全的日期处理函数
            const dateResult = formatDate(event.timestamp);
            const timeDisplay = dateResult.isValid ? dateResult.timeFormatted : '时间未知';

            // 构建事件详情
            let detailsHtml = '';
            if (event.type === 'purchase') {
                detailsHtml = `
                    <div class="timeline-details">
                        订单号: ${event.order_id || 'N/A'}，金额: ¥${event.amount ? event.amount.toFixed(2) : '0.00'}
                    </div>
                    <div class="timeline-details">
                        商品: ${event.products ? event.products.map(p => p.name).join(', ') : '未知商品'}
                    </div>
                `;
            } else if (event.type === 'view' || event.type === 'click') {
                detailsHtml = `
                    <div class="timeline-details">
                        查看了商品: ${event.product ? event.product.name : '未知商品'}
                    </div>
                `;
            } else if (event.type === 'add_to_cart') {
                detailsHtml = `
                    <div class="timeline-details">
                        添加商品到购物车: ${event.product ? event.product.name : '未知商品'}
                    </div>
                    <div class="timeline-details">
                        数量: ${event.quantity || 1}
                    </div>
                `;
            }

            // 如果日期无效，添加错误提示
            const dateErrorHtml = dateResult.isValid ? '' :
                `<div class="date-error mt-1">
                    警告: 原始时间戳无效 - ${event.timestamp}
                </div>`;

            eventElement.innerHTML = `
                <div class="flex items-start">
                    <i class="fa ${icon} mt-1 mr-3"></i>
                    <div>
                        <div class="flex items-center">
                            <div class="timeline-action">${getEventTypeName(event.type)}</div>
                            <span class="text-xs text-neutral-400 ml-2">${timeDisplay}</span>
                        </div>
                        ${detailsHtml}
                        ${dateErrorHtml}
                    </div>
                </div>
            `;

            return eventElement;
        }

        // 获取事件类型的显示名称
        function getEventTypeName(type) {
            const eventTypes = {
                'purchase': '完成购买',
                'view': '浏览商品',
                'click': '点击商品',
                'add_to_cart': '加入购物车',
                'login': '登录系统',
                'logout': '退出登录',
                'register': '注册账号',
                'review': '发表评价',
                'share': '分享商品'
            };
            return eventTypes[type] || type;
        }

        // 获取当前聚类数量
        function getClusterCount() {
            return document.getElementById('n-clusters').value;
        }

        // 获取日期范围
        function getDateRange() {
            return document.getElementById('date-range').value;
        }

        // 加载用户画像数据
        function loadUserPortraitData() {
            const nClusters = getClusterCount();
            const dateRange = getDateRange();
            const { recency, frequency, monetary } = currentThresholds;

            return fetch(`/api/user_portrait?n_clusters=${nClusters}&date_range=${dateRange}&recency_threshold=${recency}&frequency_threshold=${frequency}&monetary_threshold=${monetary}`)
                .then(response => {
                    if (!response.ok) {
                        throw new Error(`HTTP错误，状态码: ${response.status}`);
                    }
                    return response.text();
                })
                .then(text => safeJsonParse(text))
                .then(data => {
                    console.log("用户画像数据:", data);
                    window.userPortraitData = data;
                    renderTagCloud(data, 'all');
                    renderUserPortraitChart(data);
                })
                .catch(error => {
                    console.error('获取用户画像数据失败:', error);
                    showError('获取用户画像数据失败: ' + error.message);
                });
        }

        // 渲染标签云
        function renderTagCloud(data, type = 'all') {
            const tagCloud = document.getElementById('tag-cloud');
            tagCloud.innerHTML = '';

            const container = document.getElementById('tag-cloud-container');
            const width = Math.floor(container.clientWidth);
            const height = Math.floor(container.clientHeight);

            if (width <= 0 || height <= 0 || isNaN(width) || isNaN(height)) {
                console.error('无效的画布尺寸', {width, height});
                showError('标签云渲染失败：无效的尺寸');
                return;
            }

            let tags = [];

            // 确保数据存在
            if (!data || (!data.price_tags && !data.activity_tags)) {
                tagCloud.innerHTML = '<div class="flex items-center justify-center h-full text-neutral-500">无用户画像数据</div>';
                return;
            }

            // 处理价格标签
            if ((type === 'price' || type === 'all') && data.price_tags && data.price_tags.length > 0) {
                data.price_tags.forEach(tag => {
                    const weight = Math.max(1, Math.floor(Number(tag.size) || 1));
                    tags.push({
                        text: tag.text || '未知标签',
                        weight: weight,
                        color: tag.color || getRandomColor()
                    });
                });
            }

            // 处理活跃度标签
            if ((type === 'activity' || type === 'all') && data.activity_tags && data.activity_tags.length > 0) {
                data.activity_tags.forEach(tag => {
                    const weight = Math.max(1, Math.floor(Number(tag.size) || 1));
                    tags.push({
                        text: tag.text || '未知标签',
                        weight: weight,
                        color: tag.color || getRandomColor()
                    });
                });
            }

            if (tags.length === 0) {
                tagCloud.innerHTML = '<div class="flex items-center justify-center h-full text-neutral-500">暂无标签数据</div>';
                return;
            }

            const canvas = document.createElement('canvas');
            canvas.width = width;
            canvas.height = height;
            tagCloud.appendChild(canvas);

            // 渲染标签云
            WordCloud(canvas, {
                list: tags.map(tag => [tag.text, tag.weight]),
                color: (word, weight) => {
                    const tag = tags.find(t => t.text === word);
                    return tag ? tag.color : '#333';
                },
                backgroundColor: '#ffffff',
                minSize: 12,
                maxSize: 48,
                rotateRatio: 0.5,
                shape: 'circle',
                width: width,
                height: height
            });
        }

        // 生成随机颜色 - 用于标签云
        function getRandomColor() {
            const colors = [
                'rgba(22, 93, 255, 0.8)', 'rgba(255, 99, 132, 0.8)',
                'rgba(54, 207, 201, 0.8)', 'rgba(255, 125, 0, 0.8)',
                'rgba(114, 46, 209, 0.8)', 'rgba(255, 206, 86, 0.8)',
                'rgba(245, 63, 63, 0.8)', 'rgba(46, 125, 50, 0.8)'
            ];
            return colors[Math.floor(Math.random() * colors.length)];
        }

        // 渲染用户画像分布图表
        function renderUserPortraitChart(data) {
            const ctx = document.getElementById('user-portrait-chart').getContext('2d');

            if (window.userPortraitChart) {
                window.userPortraitChart.destroy();
            }

            // 确保数据存在
            if (!data || (!data.price_tags && !data.activity_tags)) {
                // 显示无数据提示
                const chartContainer = document.getElementById('user-portrait-chart').parentNode;
                chartContainer.innerHTML = '<div class="flex items-center justify-center h-full text-neutral-500">无用户画像数据</div>';
                return;
            }

            // 提取价格标签数据
            const priceLabels = data.price_tags ? data.price_tags.map(tag => tag.text || '未知标签') : [];
            const priceData = data.price_tags ? data.price_tags.map(tag => tag.size || 0) : [];
            const priceColors = data.price_tags ? data.price_tags.map(tag => tag.color || getRandomColor()) : [];

            // 提取活跃度标签数据
            const activityLabels = data.activity_tags ? data.activity_tags.map(tag => tag.text || '未知标签') : [];
            const activityData = data.activity_tags ? data.activity_tags.map(tag => tag.size || 0) : [];
            const activityColors = data.activity_tags ? data.activity_tags.map(tag => tag.color || getRandomColor()) : [];

            // 合并数据
            const allLabels = [...priceLabels, ...activityLabels];
            const allData = [...priceData, ...activityData];
            const allColors = [...priceColors, ...activityColors];

            if (allLabels.length === 0) {
                // 显示无数据提示
                const chartContainer = document.getElementById('user-portrait-chart').parentNode;
                chartContainer.innerHTML = '<div class="flex items-center justify-center h-full text-neutral-500">无用户画像数据</div>';
                return;
            }

            // 创建图表
            window.userPortraitChart = new Chart(ctx, {
                type: 'bar',
                data: {
                    labels: allLabels,
                    datasets: [{
                        label: '标签权重',
                        data: allData,
                        backgroundColor: allColors,
                        borderWidth: 1
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    scales: {
                        y: {
                            beginAtZero: true,
                            title: { display: true, text: '权重' }
                        }
                    },
                    plugins: {
                        legend: { display: false }
                    }
                }
            });
        }

        // 加载RFM分数数据
        function loadRfmData() {
            const nClusters = getClusterCount();
            const dateRange = getDateRange();
            const { recency, frequency, monetary } = currentThresholds;

            // 获取RFM分数数据
            const scorePromise = fetch(`/api/rfm_scores?n_clusters=${nClusters}&date_range=${dateRange}&recency_threshold=${recency}&frequency_threshold=${frequency}&monetary_threshold=${monetary}`)
                .then(response => {
                    if (!response.ok) {
                        throw new Error(`HTTP错误，状态码: ${response.status}`);
                    }
                    return response.text();
                })
                .then(text => safeJsonParse(text))
                .then(data => {
                    renderRfmScoreChart(data);
                })
                .catch(error => {
                    console.error('获取RFM分数失败:', error);
                    showError('获取RFM分数失败: ' + error.message);
                });

            // 获取RFM聚类数据
            const clusterPromise = fetch(`/api/rfm_clusters?n_clusters=${nClusters}&date_range=${dateRange}&recency_threshold=${recency}&frequency_threshold=${frequency}&monetary_threshold=${monetary}`)
                .then(response => {
                    if (!response.ok) {
                        if (response.status === 500) {
                            throw new Error('服务器内部错误，请稍后重试');
                        }
                        throw new Error(`HTTP错误，状态码: ${response.status}`);
                    }
                    return response.text();
                })
                .then(text => safeJsonParse(text))
                .then(data => {
                    rfmData = data; // 保存数据供后续切换维度使用
                    renderRfm2DScatterChart(data);
                })
                .catch(error => {
                    console.error('获取RFM聚类失败:', error);
                    showError('获取RFM聚类失败: ' + error.message);
                });

            return Promise.all([scorePromise, clusterPromise]);
        }

        // 加载用户分群数据
        function loadUserSegments() {
            const nClusters = getClusterCount();
            const dateRange = getDateRange();
            const { recency, frequency, monetary } = currentThresholds;

            return fetch(`/api/user_segments?n_clusters=${nClusters}&date_range=${dateRange}&recency_threshold=${recency}&frequency_threshold=${frequency}&monetary_threshold=${monetary}`)
                .then(response => {
                    if (!response.ok) {
                        throw new Error(`HTTP错误，状态码: ${response.status}`);
                    }
                    return response.text();
                })
                .then(text => safeJsonParse(text))
                .then(data => {
                    renderSegmentsCards(data);
                    renderSegmentPieChart(data);
                })
                .catch(error => {
                    console.error('获取用户分群失败:', error);
                    showError('获取用户分群失败: ' + error.message);
                });
        }

        // 渲染分群结果卡片
        function renderSegmentsCards(data) {
            const container = document.getElementById('segments-cards-container');
            container.innerHTML = '';

            if (!data || data.length === 0) {
                container.innerHTML = `
                    <div class="col-span-full flex items-center justify-center h-64 text-neutral-500">
                        暂无分群数据
                    </div>
                `;
                return;
            }

            data.sort((a, b) => (b['占比'] || 0) - (a['占比'] || 0));

            // 定义不同分群的颜色
            const colors = [
                'rgba(22, 93, 255, 1)', 'rgba(54, 207, 201, 1)',
                'rgba(255, 125, 0, 1)', 'rgba(114, 46, 209, 1)',
                'rgba(245, 63, 63, 1)', 'rgba(255, 206, 86, 1)'
            ];

            data.forEach((segment, index) => {
                const color = colors[index % colors.length];
                const percentage = segment['占比'] ? (segment['占比'] * 100).toFixed(1) : 0;
                const userCount = segment['用户数量'] ? segment['用户数量'].toLocaleString() : 0;

                // 创建进度环SVG
                const circleRadius = 30;
                const circleCircumference = 2 * Math.PI * circleRadius;
                const progress = percentage / 100;
                const strokeDashoffset = circleCircumference - (progress * circleCircumference);

                const card = document.createElement('div');
                card.className = 'bg-white border border-neutral-200 rounded-lg shadow-sm hover:shadow-md transition-all overflow-hidden';
                card.innerHTML = `
                    <div class="p-4 border-b border-neutral-100 bg-neutral-50">
                        <div class="flex justify-between items-center">
                            <h3 class="font-semibold text-neutral-800">${segment['用户标签'] || '未知分群'}</h3>
                            <div class="relative inline-block w-16 h-16">
                                <svg class="w-full h-full" viewBox="0 0 80 80">
                                    <circle
                                        cx="40" cy="40" r="${circleRadius}"
                                        fill="none"
                                        stroke="${color.replace('1)', '0.2)')}"
                                        stroke-width="6"
                                    />
                                    <circle
                                        cx="40" cy="40" r="${circleRadius}"
                                        fill="none"
                                        stroke="${color}"
                                        stroke-width="6"
                                        stroke-dasharray="${circleCircumference}"
                                        stroke-dashoffset="${strokeDashoffset}"
                                        class="progress-ring transition-all duration-1000 ease-out"
                                    />
                                </svg>
                                <div class="absolute inset-0 flex items-center justify-center text-sm font-medium">
                                    ${percentage}%
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="p-4">
                        <div class="flex justify-between items-center mb-3">
                            <span class="text-neutral-500 text-sm">用户数量</span>
                            <span class="font-medium">${userCount}</span>
                        </div>
                        <div class="flex justify-between items-center mb-3">
                            <span class="text-neutral-500 text-sm">价格敏感度</span>
                            <span class="font-medium">${segment['价格敏感度'] !== undefined ? segment['价格敏感度'].toFixed(2) : '未知'}</span>
                        </div>
                        <div class="flex justify-between items-center mb-3">
                            <span class="text-neutral-500 text-sm">活跃度</span>
                            <span class="font-medium">${segment['活跃度'] !== undefined ? segment['活跃度'].toFixed(2) : '未知'}</span>
                        </div>
                        <div class="mt-4">
                            <h4 class="text-sm font-medium text-neutral-700 mb-2">策略建议</h4>
                            <p class="text-sm text-neutral-600">${segment['策略建议'] || '暂无建议'}</p>
                        </div>
                    </div>
                    <div class="px-4 pb-4">
                        <button class="w-full py-2 text-sm bg-neutral-100 hover:bg-neutral-200 text-neutral-700 rounded-md transition-colors flex items-center justify-center">
                            <i class="fa fa-bar-chart mr-1"></i> 查看详情
                        </button>
                    </div>
                `;

                container.appendChild(card);
            });
        }

        // 渲染RFM得分分布图表
        function renderRfmScoreChart(data) {
            const ctx = document.getElementById('rfm-score-chart').getContext('2d');

            function getScoreDistribution(scores, maxScore = 5) {
                const distribution = Array(maxScore).fill(0);
                scores.forEach(score => {
                    const index = Math.min(Math.round(score) - 1, maxScore - 1);
                    distribution[index]++;
                });
                return distribution;
            }

            const rScores = data.map(item => item.R_Score);
            const fScores = data.map(item => item.F_Score);
            const mScores = data.map(item => item.M_Score || 0);

            const rDistribution = getScoreDistribution(rScores);
            const fDistribution = getScoreDistribution(fScores);
            const mDistribution = getScoreDistribution(mScores);
            const labels = Array.from({length: rDistribution.length}, (_, i) => i + 1);

            if (window.rfmScoreChart) {
                window.rfmScoreChart.destroy();
            }

            window.rfmScoreChart = new Chart(ctx, {
                type: 'bar',
                data: {
                    labels: labels,
                    datasets: [
                        {
                            label: 'R得分分布',
                            data: rDistribution,
                            backgroundColor: 'rgba(22, 93, 255, 0.5)',
                            borderColor: 'rgba(22, 93, 255, 1)',
                            borderWidth: 1
                        },
                        {
                            label: 'F得分分布',
                            data: fDistribution,
                            backgroundColor: 'rgba(245, 63, 63, 0.5)',
                            borderColor: 'rgba(245, 63, 63, 1)',
                            borderWidth: 1
                        },
                        {
                            label: 'M得分分布',
                            data: mDistribution,
                            backgroundColor: 'rgba(54, 207, 201, 0.5)',
                            borderColor: 'rgba(54, 207, 201, 1)',
                            borderWidth: 1
                        }
                    ]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    scales: {
                        y: {
                            beginAtZero: true,
                            title: { display: true, text: '用户数量' },
                            ticks: { precision: 0 }
                        },
                        x: { title: { display: true, text: '分数' } }
                    }
                }
            });
        }

        // 渲染RFM二维散点图（根据当前选择的维度）
        function renderRfm2DScatterChart(data) {
            const ctx = document.getElementById('rfm-2d-scatter-chart').getContext('2d');

            // 销毁现有图表
            if (window.rfm2DChart) {
                window.rfm2DChart.destroy();
                window.rfm2DChart = null;
            }

            // 验证数据
            if (!data || data.length === 0) {
                showError('没有可用的RFM数据来生成图表');
                return;
            }

            // 处理数据
            const clusters = {};
            data.forEach(item => {
                const cluster = item['用户标签'] || `聚类 ${item.Cluster}`;
                if (!clusters[cluster]) {
                    clusters[cluster] = {
                        recency: [],
                        frequency: [],
                        monetary: []
                    };
                }
                clusters[cluster].recency.push(item.Recency);
                clusters[cluster].frequency.push(item.Frequency);
                clusters[cluster].monetary.push(item.Monetary || 0);
            });

            // 数据归一化处理
            function normalizeData(values) {
                const min = Math.min(...values);
                const max = Math.max(...values);
                return values.map(v => (v - min) / (max - min || 1));
            }

            // 根据当前选择的维度组合准备数据
            let xLabel, yLabel;
            let xAccessor, yAccessor;

            switch(currentDimensions) {
                case 'recency-frequency':
                    xLabel = '最近购买 (Recency)';
                    yLabel = '购买频率 (Frequency)';
                    xAccessor = 'recency';
                    yAccessor = 'frequency';
                    break;
                case 'recency-monetary':
                    xLabel = '最近购买 (Recency)';
                    yLabel = '消费金额 (Monetary)';
                    xAccessor = 'recency';
                    yAccessor = 'monetary';
                    break;
                case 'frequency-monetary':
                    xLabel = '购买频率 (Frequency)';
                    yLabel = '消费金额 (Monetary)';
                    xAccessor = 'frequency';
                    yAccessor = 'monetary';
                    break;
                default:
                    xLabel = '最近购买 (Recency)';
                    yLabel = '购买频率 (Frequency)';
                    xAccessor = 'recency';
                    yAccessor = 'frequency';
            }

            // 准备数据集
            const datasets = Object.entries(clusters).map(([cluster, values], index) => {
                const colors = [
                    'rgba(54, 207, 201, 1)', 'rgba(22, 93, 255, 1)',
                    'rgba(245, 63, 63, 1)', 'rgba(255, 125, 0, 1)',
                    'rgba(114, 46, 209, 1)', 'rgba(255, 206, 86, 1)',
                    'rgba(231, 233, 237, 1)', 'rgba(108, 117, 125, 1)',
                    'rgba(220, 53, 69, 1)', 'rgba(25, 135, 84, 1)'
                ];
                const color = colors[index % colors.length];

                // 归一化数据
                const normalizedX = normalizeData(values[xAccessor]);
                const normalizedY = normalizeData(values[yAccessor]);

                return {
                    label: cluster,
                    data: normalizedX.map((x, i) => ({
                        x: x,
                        y: normalizedY[i]
                    })),
                    backgroundColor: color.replace('1)', '0.7)'),
                    borderColor: color,
                    borderWidth: 1,
                    pointRadius: 4,
                    pointHoverRadius: 6
                };
            });

            try {
                // 创建2D散点图
                window.rfm2DChart = new Chart(ctx, {
                    type: 'scatter',
                    data: { datasets },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        scales: {
                            x: {
                                type: 'linear',
                                position: 'bottom',
                                title: {
                                    display: true,
                                    text: xLabel
                                },
                                ticks: {
                                    callback: function(value) {
                                        // 找到该归一化值对应的原始数据范围
                                        const allValues = Object.values(clusters).flatMap(v => v[xAccessor]);
                                        const min = Math.min(...allValues);
                                        const max = Math.max(...allValues);
                                        const actualValue = min + (max - min) * value;

                                        // 根据维度类型格式化显示
                                        if (xAccessor === 'recency') {
                                            return `${Math.round(actualValue)}天`;
                                        } else if (xAccessor === 'frequency') {
                                            return `${Math.round(actualValue)}次`;
                                        } else { // monetary
                                            return `¥${Math.round(actualValue)}`;
                                        }
                                    }
                                }
                            },
                            y: {
                                title: {
                                    display: true,
                                    text: yLabel
                                },
                                ticks: {
                                    callback: function(value) {
                                        // 找到该归一化值对应的原始数据范围
                                        const allValues = Object.values(clusters).flatMap(v => v[yAccessor]);
                                        const min = Math.min(...allValues);
                                        const max = Math.max(...allValues);
                                        const actualValue = min + (max - min) * value;

                                        // 根据维度类型格式化显示
                                        if (yAccessor === 'recency') {
                                            return `${Math.round(actualValue)}天`;
                                        } else if (yAccessor === 'frequency') {
                                            return `${Math.round(actualValue)}次`;
                                        } else { // monetary
                                            return `¥${Math.round(actualValue)}`;
                                        }
                                    }
                                }
                            }
                        },
                        plugins: {
                            tooltip: {
                                callbacks: {
                                    label: function(context) {
                                        const idx = context.dataIndex;
                                        const dataset = context.dataset;
                                        const clusterName = dataset.label;
                                        const clusterData = Object.entries(clusters).find(([name]) => name === clusterName)[1];

                                        // 获取原始值
                                        const xValue = clusterData[xAccessor][idx];
                                        const yValue = clusterData[yAccessor][idx];

                                        // 格式化显示
                                        let xDisplay, yDisplay;

                                        if (xAccessor === 'recency') {
                                            xDisplay = `${xValue}天前`;
                                        } else if (xAccessor === 'frequency') {
                                            xDisplay = `${xValue}次`;
                                        } else {
                                            xDisplay = `¥${xValue.toFixed(2)}`;
                                        }

                                        if (yAccessor === 'recency') {
                                            yDisplay = `${yValue}天前`;
                                        } else if (yAccessor === 'frequency') {
                                            yDisplay = `${yValue}次`;
                                        } else {
                                            yDisplay = `¥${yValue.toFixed(2)}`;
                                        }

                                        return [
                                            `${clusterName}`,
                                            `${xLabel}: ${xDisplay}`,
                                            `${yLabel}: ${yDisplay}`
                                        ];
                                    }
                                }
                            }
                        }
                    }
                });

                console.log('2D散点图初始化成功');

            } catch (error) {
                console.error('2D散点图初始化失败:', error);
                showError('散点图加载失败: ' + error.message + '，请尝试刷新页面');
                window.rfm2DChart = null;
            }
        }

        // 渲染分群占比饼图
        function renderSegmentPieChart(data) {
            const ctx = document.getElementById('segment-pie-chart').getContext('2d');

            if (window.segmentPieChart) {
                window.segmentPieChart.destroy();
            }

            if (!data || data.length === 0) {
                return;
            }

            data.sort((a, b) => (b['占比'] || 0) - (a['占比'] || 0));

            const labels = data.map(item => item['用户标签'] || '未知');
            const values = data.map(item => item['占比'] || 0);

            const colors = [
                'rgba(54, 207, 201, 0.7)', 'rgba(22, 93, 255, 0.7)',
                'rgba(245, 63, 63, 0.7)', 'rgba(255, 125, 0, 0.7)',
                'rgba(114, 46, 209, 0.7)', 'rgba(255, 206, 86, 0.7)',
                'rgba(231, 233, 237, 0.7)', 'rgba(108, 117, 125, 0.7)',
                'rgba(220, 53, 69, 0.7)', 'rgba(25, 135, 84, 0.7)'
            ];

            window.segmentPieChart = new Chart(ctx, {
                type: 'pie',
                data: {
                    labels,
                    datasets: [{
                        data: values,
                        backgroundColor: colors.slice(0, labels.length),
                        borderColor: colors.slice(0, labels.length).map(c => c.replace('0.7', '1')),
                        borderWidth: 1
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    plugins: {
                        legend: { position: 'right' },
                        tooltip: {
                            callbacks: {
                                label: function(context) {
                                    const value = context.raw;
                                    return `${context.label}: ${(value * 100).toFixed(2)}%`;
                                }
                            }
                        }
                    }
                }
            });
        }
    </script>


    </body></html>
